#include <windows.h>
#include <stdio.h>
#include <iostream>


using std::cout;
using std::endl;
using std::hex;
using std::dec;



uint8_t getBit(uint8_t number, bool position) {
    uint8_t bitmask = 1 << position;
    return (number & bitmask) ? 1 : 0;
}

void printGdt(char* const buffer, size_t size) {
    size_t currentOffset = 0;
    size_t counter = 0;
    while (currentOffset < size) {
        const uint16_t limit0_15 = *(reinterpret_cast<const uint16_t*>(buffer + currentOffset));
        currentOffset += 2;
        const uint16_t base0_15 = *(reinterpret_cast<const uint16_t*>(buffer + currentOffset));
        currentOffset += 2;
        const uint8_t base16_23 = *(reinterpret_cast<const uint8_t*>(buffer + currentOffset));
        currentOffset += 1;
        const uint8_t accessByte = *(reinterpret_cast<const uint8_t*>(buffer + currentOffset));
        currentOffset += 1;
        const uint8_t limitAndFlags = *(reinterpret_cast<const uint8_t*>(buffer + currentOffset));
        currentOffset += 1;
        const uint8_t base24_31 = *(reinterpret_cast<const uint8_t*>(buffer + currentOffset));
        currentOffset += 1;

        const uint8_t SegmentType1 = getBit(accessByte, 0);
        const uint8_t SegmentType2 = getBit(accessByte, 1);
        const uint8_t SegmentType3 = getBit(accessByte, 2);
        const uint8_t SegmentType4 = getBit(accessByte, 3);
        const uint8_t SegmentType = SegmentType1 + 2*SegmentType2 + 4*SegmentType3 + 8*SegmentType4;
        const uint8_t DescriptorType = getBit(accessByte, 4);
        const uint8_t dpl1 = getBit(accessByte, 5);
        const uint8_t dpl2 = getBit(accessByte, 6);
        const uint8_t dpl = dpl1 + 2*dpl2;
        const uint8_t segmentPresent = getBit(accessByte, 7);

        const uint8_t limit16 = getBit(limitAndFlags, 0);
        const uint8_t limit17 = getBit(limitAndFlags, 1);
        const uint8_t limit18 = getBit(limitAndFlags, 2);
        const uint8_t limit19 = getBit(limitAndFlags, 3);
        const uint8_t limit16_19 = limit16 + 2*limit17 + 4*limit18 + 8*limit19;

        const uint8_t avl = getBit(limitAndFlags, 4);
        const uint8_t x64Flag = getBit(limitAndFlags, 5);
        const uint8_t defaultOperationSize = getBit(limitAndFlags, 6);
        const uint8_t granularity = getBit(limitAndFlags, 7);

        cout << "--------------------------------------------" << endl;
        cout << "Descriptor #" << counter << ":" << endl;
        cout << hex
            << "\tLimit 0:15 = " << limit0_15 << endl
            << "\tBase 0:15 = " << base0_15 << endl
            << "\tBase 16:23 = " << (uint16_t)base16_23 << endl
            << "\tSegmentType = " << (uint16_t)SegmentType << endl
            << "\tDescriptorType = " << (uint16_t)DescriptorType << endl
            << "\tDPL = " << (uint16_t)dpl << endl
            << "\tSegmentPresent = " << (uint16_t)segmentPresent << endl
            << "\tLimit 16:19 = " << (uint16_t)limit16_19 << endl
            << "\tAVL = " << (uint16_t)avl << endl
            << "\tx64Flag = " << (uint16_t)x64Flag << endl
            << "\tDefaultOperationSize = " << (uint16_t)defaultOperationSize << endl
            << "\tGranularity = " << (uint16_t)granularity << endl;
        cout << "--------------------------------------------" << endl;

        ++counter;
    }
}


void printEntry(LDT_ENTRY entry, DWORD selectorAddress) {

    cout << "--------------------------------------------" << endl;
    cout << "Ldt entry for " << selectorAddress << ":" << endl;
    cout << hex
        << "\tLimitLow = " << (uint16_t)entry.LimitLow << endl
        << "\tBaseLow = " << (uint16_t)entry.BaseLow << endl
        << "\tBytes.BaseMid = " << (uint16_t)entry.HighWord.Bytes.BaseMid << endl
        << "\tFlags1 = " << (uint16_t)entry.HighWord.Bytes.Flags1 << endl
        << "\tFlags2 = " << (uint16_t)entry.HighWord.Bytes.Flags2 << endl
        << "\tBytes.BaseHi = " << (uint16_t)entry.HighWord.Bytes.BaseHi << endl
        << "\tBits.BaseMid = " << (uint16_t)entry.HighWord.Bits.BaseMid << endl
        << "\tType = " << (uint16_t)entry.HighWord.Bits.Type << endl
        << "\tDpl = " << (uint16_t)entry.HighWord.Bits.Dpl << endl
        << "\tPres = " << (uint16_t)entry.HighWord.Bits.Pres << endl
        << "\tLimitHi = " << (uint16_t)entry.HighWord.Bits.LimitHi << endl
        << "\tSys = " << (uint16_t)entry.HighWord.Bits.Sys << endl
        << "\tReserved_0 = " << (uint16_t)entry.HighWord.Bits.Reserved_0 << endl
        << "\tDefault_Big = " << (uint16_t)entry.HighWord.Bits.Default_Big << endl
        << "\tGranularity = " << (uint16_t)entry.HighWord.Bits.Granularity << endl
        << "\tBits.BaseHi = " << (uint16_t)entry.HighWord.Bits.BaseHi << endl;
    cout << "--------------------------------------------" << endl;
}

bool operator!=(const LDT_ENTRY& fst, const LDT_ENTRY& snd) {
    return  fst.LimitLow != snd.LimitLow ||
            fst.BaseLow != snd.BaseLow ||
            fst.HighWord.Bytes.BaseMid != snd.HighWord.Bytes.BaseMid ||
            fst.HighWord.Bytes.Flags1 != snd.HighWord.Bytes.Flags1 ||
            fst.HighWord.Bytes.Flags2 != snd.HighWord.Bytes.Flags2 ||
            fst.HighWord.Bytes.BaseHi != snd.HighWord.Bytes.BaseHi ||
            fst.HighWord.Bits.BaseMid != snd.HighWord.Bits.BaseMid ||
            fst.HighWord.Bits.Type != snd.HighWord.Bits.Type ||
            fst.HighWord.Bits.Dpl != snd.HighWord.Bits.Dpl ||
            fst.HighWord.Bits.Pres != snd.HighWord.Bits.Pres ||
            fst.HighWord.Bits.LimitHi != snd.HighWord.Bits.LimitHi ||
            fst.HighWord.Bits.Sys != snd.HighWord.Bits.Sys ||
            fst.HighWord.Bits.Reserved_0 != snd.HighWord.Bits.Reserved_0 ||
            fst.HighWord.Bits.Default_Big != snd.HighWord.Bits.Default_Big ||
            fst.HighWord.Bits.Granularity != snd.HighWord.Bits.Granularity ||
            fst.HighWord.Bits.BaseHi != snd.HighWord.Bits.BaseHi;
}  

int main()
{
    HANDLE hFile;
    DWORD dwReturn;
    long szTemp = 0;
    char* buffer = (char*)malloc(1024);

    hFile = CreateFile(TEXT("\\\\.\\MyDevice"), GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_ALWAYS, 0, NULL);

    if (hFile)
    {
        cout << "file created" << endl;
        
        ReadFile(hFile, buffer, 1024, &dwReturn, NULL);
        cout << "GDT: " << endl;
        printGdt(buffer, 1024);
        printf("\n");
                
        CloseHandle(hFile);
    }

    CONTEXT context = {};
    context.ContextFlags = CONTEXT_FULL;

    HANDLE currrentThread = GetCurrentThread();
    GetThreadContext(currrentThread, &context);

    cout << "LDT : " << endl;

    LDT_ENTRY ldtEntry = {};

    for (int i = 0; i <= 1024; ++i) {
        LDT_ENTRY prevLdtEntry = ldtEntry;
        GetThreadSelectorEntry(currrentThread, i, &ldtEntry);
        if (GetLastError() == 0 && prevLdtEntry != ldtEntry) {
            printEntry(ldtEntry, i);
            cout << GetLastError() << endl;
        }
        SetLastError(0);
    }

    cout << dec << sizeof(LDT_ENTRY) << endl;
 
    return 0;
}